package com.czk.classanlysis;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;

import java.util.ArrayList;
import java.util.List;

import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_NATIVE;

/**
 * 查看方法在一个类中被哪些方法调用了
 *
 * @Author:ChenZhangKun
 * @Date: 2021/8/26 17:55
 */
public class MethodFindRefVisitor extends ClassVisitor {
    // 方法所在的类
    private final String methodOwner;
    // 方法名称
    private final String methodName;
    // 方法描述
    private final String methodDesc;
    // 当前正在处理类的名字
    private String owner;
    private final List<String> list = new ArrayList<>();

    public MethodFindRefVisitor(int api, ClassVisitor classVisitor, String methodOwner, String methodName, String methodDesc) {
        super(api, classVisitor);
        this.methodOwner = methodOwner;
        this.methodName = methodName;
        this.methodDesc = methodDesc;
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);
        this.owner = name;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;
        boolean isNativeMethod = (access & ACC_NATIVE) != 0;
        if (!isAbstractMethod && !isNativeMethod) {
            return new MethodFindRefAdaptor(api, null, owner, name, descriptor);
        }
        return null;
    }

    @Override
    public void visitEnd() {
        for (String s : list) {
            System.out.println(s);
        }
        super.visitEnd();
    }

    /**
     * 收集数据
     */
    private class MethodFindRefAdaptor extends MethodVisitor {
        // 当前正在处理的方法所有者、名称、和方法描述
        private final String currentMethodOwner;
        private final String currentMethodName;
        private final String currentMethodDesc;

        public MethodFindRefAdaptor(int api, MethodVisitor methodVisitor, String currentMethodOwner, String currentMethodName, String currentMethodDesc) {
            super(api, methodVisitor);
            this.currentMethodOwner = currentMethodOwner;
            this.currentMethodName = currentMethodName;
            this.currentMethodDesc = currentMethodDesc;
        }

        /**
         * 在这里收集数据
         *
         * @param opcode
         * @param owner
         * @param name
         * @param descriptor
         * @param isInterface
         */
        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
            // 当前正在处理方法是否对传入的方法进行了调用
            if (methodOwner.equals(owner) && methodName.equals(name) && methodDesc.equals(descriptor)) {
                String info = String.format("%s.%s%s", currentMethodOwner, currentMethodName, currentMethodDesc);
                if (!list.contains(info)) {
                    list.add(info);
                }
                super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
            }
        }
    }
}
